#include "GlobalMessages.h"

#include <QListView>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QIcon>
#include <QPicture>
#include <QAction>
#include <QPushButton>
#include <QCheckBox>
#include <QSplitter>
#include <QDockWidget>
#include <QEvent>

#include <iostream>

using namespace simodo;

static constexpr int MAX_COUNT_HINT = 1000;
static constexpr int MAX_COUNT = 2 * MAX_COUNT_HINT;
static constexpr int DELAY_INTERVAL = 100;

GlobalMessages::GlobalMessages(QDockWidget & dock_widget)
    : _dock_widget(dock_widget)
    , _model(MAX_COUNT_HINT)
{
    _list   = new GlobalMessageView;
    _list->setWordWrap(true);
    _list->setModel(&_model);
    _list->setSpacing(0);

    _layout = new QVBoxLayout(this);
    _layout->addWidget(_list);

    setContentsMargins(0,0,0,0);
    _layout->setMargin(0);
    _layout->setSpacing(0);
    _layout->setContentsMargins(0,0,0,0);

    setPaletteColors(palette());

    resetTimer();

    _message_buffer.reserve(MAX_COUNT);
}

void GlobalMessages::sendGlobal(const QString & text, shell::MessageSeverity severity)
{
    if (MAX_COUNT < _message_buffer.size() + 1) {
        // _model.clear();
        // _message_buffer.clear();
        _message_buffer.erase(_message_buffer.begin(), _message_buffer.begin() + MAX_COUNT_HINT);
        // flushBuffer();
    }
    _message_buffer.push_back({text, severity});
}

void GlobalMessages::askGlobal(shell::MessageSeverity severity, const QString & title, const QString & description, 
                   std::vector<AskButton> buttons, const QString & group_name)
{
    GlobalAsk ask {severity, title, description, buttons, group_name};

    auto it = std::find_if(_ask_queue.begin(),_ask_queue.end(), [ask](const GlobalAsk & a){
        return a.title == ask.title && a.description == ask.description && a.group_name == ask.group_name;
    });

    if (it != _ask_queue.end()) {
        sendGlobal("The same askGlobal call ignored", shell::MessageSeverity::Debug);
        return;
    }

    _ask_queue.push_back(ask);
    if (_ask_widget == nullptr)
        showAsk();

    _dock_widget.show();
    _dock_widget.raise();
}

// ---------------------------------------------------------------------------------------------------
// protected -----------------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------------------

void GlobalMessages::timerEvent(QTimerEvent * event)
{
    if (event->timerId() == _delay_timer) {
        // killTimer(_delay_timer);
        // _delay_timer = 0;

        flushBuffer();
        return;
    }

    QWidget::event(event);
}

bool GlobalMessages::event(QEvent * event)
{
    if (event->type() == QEvent::PaletteChange)
    {
        setPaletteColors(palette());
        // for (int i = 0; i < _model.rowCount(); ++i)
        // {
        //     QStandardItem * item = _model.item(i);
        //     shell::MessageSeverity severity = static_cast<shell::MessageSeverity>(item->data().toInt());
        //     setupItemAndGetPrefix(item, severity);
        // }
    }

    return QWidget::event(event);
}

// ---------------------------------------------------------------------------------------------------
// private -------------------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------------------

QString GlobalMessages::setupItemAndGetPrefix(QStandardItem * /*item*/, shell::MessageSeverity severity)
{
    // Q_ASSERT(item);

    QString prefix;
    switch(severity)
    {
    case shell::MessageSeverity::Debug:
        // item->setIcon(_debug_icon);
        // item->setForeground(_debug_foreground);
        prefix = tr("Debug: ");
        break;
    case shell::MessageSeverity::Info:
        // item->setIcon(_info_icon);
        // item->setForeground(_info_foreground);
        break;
    case shell::MessageSeverity::Warning:
        // item->setIcon(_warning_icon);
        // item->setForeground(_warning_fForeground);
        prefix = tr("Warning: ");
        break;
    case shell::MessageSeverity::Error:
        // item->setIcon(_error_icon);
        // item->setForeground(_error_foreground);
        prefix = tr("Error: ");
        break;
    }

    return prefix;
}

void GlobalMessages::setPaletteColors(const QPalette & palette)
{
    GlobalMessageModel::Palette p;
    if (palette.window().color().toCmyk().black() > 128) {
        p.foregrounds.debug   = QColorConstants::DarkGray;
        p.foregrounds.info    = QColorConstants::Gray;
        p.foregrounds.warning = QColorConstants::Yellow;
        p.foregrounds.error   = QColorConstants::Red;
    }
    else {
        p.foregrounds.debug   = QColorConstants::DarkGray;
        p.foregrounds.info    = QColorConstants::DarkBlue;
        p.foregrounds.warning = QColorConstants::DarkCyan;
        p.foregrounds.error   = QColorConstants::DarkRed;
    }

    p.icons.debug = p.icons.info = QIcon::fromTheme("dialog-information", QIcon(":/images/dialog-information"));
    _warning_icon = p.icons.warning = QIcon::fromTheme("dialog-warning", QIcon(":/images/dialog-warning"));
    p.icons.error = QIcon::fromTheme("dialog-error", QIcon(":/images/dialog-error"));

    _model.setPalette(p);
}

void GlobalMessages::flushBuffer()
{
    if (_message_buffer.empty())
    {
        return;
    }

    for(const auto & m : qAsConst(_message_buffer))
    {
        QString prefix;
        switch(m.severity)
        {
        case shell::MessageSeverity::Debug:
            prefix = tr("Debug: ");
            break;
        case shell::MessageSeverity::Info:
            break;
        case shell::MessageSeverity::Warning:
            prefix = tr("Warning: ");
            break;
        case shell::MessageSeverity::Error:
            prefix = tr("Error: ");
            break;
        }
        std::cout << prefix.toStdString() << m.text.toStdString() << '\n';
    }
    std::cout.flush();

    _model.append(_message_buffer);
    _message_buffer.clear();

    if (_list != nullptr)
    {
        _list->scrollToBottom();
    }
}

void GlobalMessages::addItemToModel(const QString & text, shell::MessageSeverity severity) 
{
    // QStandardItem * item   = new QStandardItem;
    QString         prefix = setupItemAndGetPrefix(nullptr/*item*/, severity);

    std::cout << prefix.toStdString() << text.toStdString() << std::endl;

    if (_list == nullptr) {
        // delete item;
        return;
    }

    // item->setText(text);
    // item->setData(static_cast<int>(severity));
    // item->setEditable(false);
    // QStandardItem * parent_item = _model.invisibleRootItem();
    // parent_item->appendRow(item);
    // if (_model.rowCount() > 1000) {
    //     QStandardItem * item = _model.takeItem(0);
    //     if (item)
    //         delete item;
    // }
    // _list->scrollToBottom();
}

void GlobalMessages::resetTimer()
{
    if (_delay_timer > 0)
        killTimer(_delay_timer);

    _delay_timer = startTimer(DELAY_INTERVAL);
}

void GlobalMessages::showAsk()
{
    if (_ask_widget || _ask_queue.empty())
        return;

    GlobalAsk ask = _ask_queue.front();
    _ask_queue.pop_front();

    if (!ask.group_name.isEmpty()) {
        auto it = _group_action.find(ask.group_name);
        if (it != _group_action.end()) {
            size_t i = it->second;
            if (i < ask.buttons.size() && ask.buttons[i].on_click_function) {
                ask.buttons[i].on_click_function(i);
                return;
            }
        }
    }

    _ask_widget = new QWidget;

    QLabel *    picture     = new QLabel;
    QLabel *    title       = new QLabel("<p><b>" + ask.title + "</b></p>");
    QLabel *    description = new QLabel("<p>" + ask.description + "</p>");

    _auto_repeat = new QCheckBox(tr("Repeat the selection in all similar situations"));

    picture->setPixmap(_warning_icon.pixmap(_warning_icon.actualSize(QSize(64,64))));
    title->setWordWrap(true);
    title->setToolTip(title->text());
    description->setWordWrap(true);
    description->setToolTip(description->text());
    _auto_repeat->setVisible(!ask.group_name.isEmpty());
    _auto_repeat->setFocusPolicy(Qt::NoFocus);

    QVBoxLayout * text_layout = new QVBoxLayout;
    text_layout->addWidget(title);
    text_layout->addWidget(description);
    text_layout->addWidget(_auto_repeat);

    QHBoxLayout * image_layout = new QHBoxLayout;
    image_layout->addWidget(picture);
    image_layout->addLayout(text_layout);
    image_layout->addStretch();

    QHBoxLayout * buttons_layout = new QHBoxLayout;

    for(size_t i=0; i < ask.buttons.size(); ++i) {
        AskButton & ask_button = ask.buttons[i];
        QPushButton * b = new QPushButton;
        b->setText(ask_button.title);
        b->setToolTip(ask_button.description);
        b->setFocusPolicy(Qt::NoFocus);
        buttons_layout->addWidget(b);
        connect(b, &QPushButton::clicked, this, [this,ask,i](bool ){
            if (ask.buttons[i].on_click_function) {
                if (ask.buttons[i].on_click_function(i)) {
                    closeAsk();
                    if (_auto_repeat->isVisible() && _auto_repeat->isChecked())
                        _group_action.insert({ask.group_name, i});
                }
            }
            else
                closeAsk();
        });
    }
    buttons_layout->addStretch();

    QVBoxLayout * ask_layout = new QVBoxLayout(_ask_widget);
    
    ask_layout->addLayout(image_layout);
    ask_layout->addLayout(buttons_layout);

    _layout->addWidget(_ask_widget);
}

void GlobalMessages::closeAsk()
{
    _layout->removeWidget(_ask_widget);
    delete _ask_widget;
    _ask_widget = nullptr;

    showAsk();
}

